A React Native app for the consultants a staffing agency places into client engagements: developers and designers billing hours across several projects a day, where every hour is both an invoice line and a paycheck line. One-tap capture, receipt scanning, and approval before invoicing, wrapped in a tactile neumorphic system where every control reads as physically pressable.
The people using this app are the consultants a staffing agency places into client engagements: developers, designers and marketers, mostly at a desk, frequently remote, and often moving between two or three client projects in a single day. Their hours are not an administrative formality. Every hour is simultaneously a line on a client invoice and a line on a contractor's paycheck.
That dual role is what makes the record unforgiving. An hour booked to the wrong project is a billing dispute on one side and a payroll correction on the other, and both land on the agency. So the requirements are the ones Bullhorn Time & Expense set for the staffing industry: time tied to a specific job and cost code, corrections flagged for review rather than silently overwritten, expenses captured with a receipt at the point of spend, and an approval step that clears before anything reaches invoicing.
The real adversary here is not the clock, it is recall. A developer who rebuilds the week from memory on Friday afternoon rounds everything to the half hour, forgets the forty-minute production incident, and drops the context switch between clients entirely. Those missing hours are invisible: unbilled revenue for the agency and unpaid work for the contractor. Capture has to happen in the moment, in one tap, or it does not happen accurately at all.
And it can never fail quietly. A dropped VPN, hotel wifi, a train tunnel, a laptop that slept through a submit: none of those are allowed to cost someone an hour. A punch is recorded locally first and reconciled with the server later, so the app is never the reason time went unrecorded.
I owned the product surface and the client architecture, from the design system through to the offline sync layer.
Neumorphism has a single rule: the entire interface is one tone, and depth comes from a paired shadow, light from the top left and dark from the bottom right. Nothing is outlined. A control is either extruded, meaning you can press it, or recessed, meaning it holds content or is already pressed. Every specimen below is live CSS, not an image.
Sign in and know your status. Punch in against a client and project. Work the session. Submit, expense, and get approved. The dark variant is reserved for the two screens that stay open longest, sign-in and the running timer, where a dimmer surface is easier on the eyes across an early start or a late release.
Not a fashion choice, a shape choice. Screens want one round trip for exactly the data they render. Commands want verbs, status codes and multipart uploads. Both hit the same store, so the split is a transport decision rather than a data split.
The dashboard needs the user, week totals and recent activity together. One request returns that exact shape, with no over-fetching and no waterfall.
Punching, submitting a week, uploading a receipt and approving are discrete and side-effecting. They map cleanly onto HTTP, including multipart for receipt images.
Redux Toolkit with redux-persist. Auth, punch and sync survive a cold start, because those three are what an offline punch depends on.
A billable hour cannot depend on a request succeeding. Developers work over VPNs that drop mid-session, hotel and conference wifi, tethered phones and trains, and a failed POST must never be the reason an hour disappears. Every punch is recorded locally first, queued if the post fails, and reconciled with the server identity once the queue drains.
The honest tradeoff: neumorphism is inherently low-contrast. It is a single tone doing all the work, which is exactly what makes it feel tactile and exactly what makes it a real accessibility risk, most of all for anyone with low vision and for the one-handed glance between meetings. I leaned on it because this app has very few controls per screen and one obvious action on each, so shape can carry the hierarchy that color usually would. But it does not get to ship alone. A high-contrast accessibility mode sits on the hardening list next to real JWT validation and background geofencing, and I would treat it as a release blocker rather than an enhancement.